﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Dwarf.Managed
{
    /// <summary>Solve the MapReduce problem</summary>
    //
    public class MapReduceBaseMethod
    {
        private string content;                                                     // content for parsing
        private List<String> stringEntries = new List<String>();                    // auxillary list for counting the of chars in the content
        private Dictionary<String, int> stringTotal = new Dictionary<String, int>();// final dictionary for counting the entries of chars in the content

        /// <summary>Constract a new object of the MapReduceBaseMethod class</summary>
        /// <param name="content">String to be parsed by BeginMethod()</param>
        ///
        public MapReduceBaseMethod(string content)
        {
            this.content = content;
        }

	    /// <summary>Runs method Map and method Reduce serially</summary>
        ///
        public void BeginMethod()
        {
            Monitor.Enter(stringTotal);
            FunctionMap();
            FunctionReduce();
            Monitor.Exit(stringTotal);
        }

        /// <summary>Returns 'stringTotal' Dictionary</summary>
        ///
        public Dictionary<String, int> GetStringTotal()
        {
            return stringTotal;
        }

        /// <summary>Traverses the string 'content'. Collect each word as substring of 'content'. Change the 'stringEntries' value</summary>
        ///
        public void FunctionMap()
        {
            int count = 0;                                  			    // local variable for storing the length of each word
            Func<char, bool> entryCriteria = (char ch) => (ch > 47 && ch < 58) ||   // a char has to be included to [0..9]
                                                          (ch > 96 && ch < 123) ||  // a char has to be included to [a..z]
                                                          (ch > 64 && ch < 91) ||   // a char has to be included to [A..Z]
                                                          ch == 95 ||               // a char has to be equal '_'
                                                          ch == 45 ||               // a char has to be equal '-'
                                                          ch == 39;                 // a char has to be equal '''
            for (int i = 0; i < content.Length; i++)
            {
                char ch = content[i];
                if ((ch > 47 && ch < 58) ||   // a char has to be included to [0..9]
                    (ch > 96 && ch < 123) ||  // a char has to be included to [a..z]
                    (ch > 64 && ch < 91) ||   // a char has to be included to [A..Z]
                    ch == 95 ||               // a char has to be equal '_'
                    ch == 45 ||               // a char has to be equal '-'
                    ch == 39)					    // defining the bounds of each word (char-to-char)
                {
                    count++;                                                        // getting the length of a string
                }
                else								    // find a word separator
                {
                    if (count > 0)
                    {
                        stringEntries.Add(content.Substring(i - count, count));     // add string
                    }
                    stringEntries.Add(content.Substring(i, 1));                     // add single-char string
                    count = 0;                                                      // start calculating of length of a new word
                }
            }
            if (count > 0) {
                stringEntries.Add(content.Substring(content.Length - count, count));     // add string
            }
        }

        /// <summary>Groups 'stringEntries' elements and count the number of entry of each word (entry of word)</summary>
        ///
        public void FunctionReduce()
        {
            stringEntries.Sort(String.CompareOrdinal);
            int count = 1;                                    			    // local variable for storing the count of each word
            for (int i = 1; i < stringEntries.Count; i++)
            {
                if (stringEntries[i - 1] == (stringEntries[i]))
                {                                             			    // checking the equivalence of adjoining words
                    count++;                                                        // getting count of the word
                }
                else
                {
                    stringTotal.Add(stringEntries[i - 1], count);         // add to the final list
                    count = 1;                                                      // start the next word's count
                }
            }
            stringTotal.Add(stringEntries[stringEntries.Count - 1], count);
        }
    }
}
